/***
* ==++==
*
* Copyright (c) Microsoft Corporation.  All rights reserved.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* comm.h
*
* C++ Actors: Low-level Communication Message Definitions
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/

#pragma once

#include <string>
#include <vector>

#include "actor_public.h"

namespace actors
{
    //
    // Message types used for the low-level communication between actors
    // that all other messaging is based on.
    //

    /// <summary>
    /// The set of commands that are sent at the low-level communication layer.   
    /// </summary>
    enum msg_op { CMD_NONE,
                  CMD_START,		    // Send the go-ahead message to a host
                  CMD_CLOSE,	        // Close the connection between two hosts
                  CMD_PING,		        // Check whether the host is up and the actor is available
                  CMD_SPAWN, 		    // Spawn a new actor
                  CMD_SPAWNLINK,	    // Spawn a new actor and link the creating actor to it
                  CMD_LINK,		        // Link to an already existing actor
                  CMD_RESPAWN,          // Re-spawn an actor after a fault
                  CMD_LAND,             // After a move, re-spawn an actor in a new location
                  CMD_MOVE,             // Foce a move to a new location
                  CMD_MESSAGE,		    // Forward data to an actor's inbox
                  CMD_BODY,             // A block of data following a message header
                  ACK_OK,               // Things worked as expected
                  ACK_CAN_COMPRESS,     // Special acknowledgement to a START message: OK to compress message bodies
                  ACK_BAD_COMMAND,      // Not a valid command
                  ACK_COMM_ERROR,       // Something didn't work during communication
                  ACK_BAD_DATA,         // Cannot deserialize message payload
                  ACK_NO_DECOMPRESS,    // The receiver cannot decompress a compressed message
                  ACK_DUPLICATE_SPAWN,  // The actor already exists
                  ACK_ACTR_FAULTED,     // The actor has faulted
                  ACK_ACTR_NOT_MOVED,	// The actor could not be moved
                  ACK_ACTR_MOVED,       // The actor has moved
                  ACK_NO_ACTOR          // The actor was not found.
    };

    enum msg_flags { MSGFLG_NONE       = 0,  // Nothing special's going on...
                     MSGFLG_COMPRESSED = 1,  // The message block was compressed by the sender
                     MSGFLG_TRACE      = 2,  // The sender requested that this block be traced
                     MSGFLG_LASTBLOCK  = 4   // This is the last block in a series.
    };

    //
    // Reason for a process' exit.
    //
    /// <summary>
    /// 
    /// </summary>
    enum exit_reason { Exit = 0, 	   // Normal exit
                      Migration = 1,   // actor migration
                      Suspension = 2,  // actor suspension
                      Exception = 4,   // An exception was uncaught by the process
                      Error = 8,	   // An error flagged by user code
                      Propagated = 16, // A propagated error, sent when there is no handler for an incoming fault
    };

    enum already_faulted_exc { None };

    /// <summary>
    /// A C++ wrapper around the Win32 GUID struct.
    /// </summary>
    struct msg_guid
    {
        _DISPIMP msg_guid();
        msg_guid(const GUID &guid) : guid(guid) {}
        msg_guid(const msg_guid &other) : guid(other.guid) {}

        _DISPIMP bool operator == (const msg_guid &other) const; 
        _DISPIMP bool operator != (const msg_guid &other) const; 
        _DISPIMP bool operator  < (const msg_guid &other) const; 
        _DISPIMP bool operator  > (const msg_guid &other) const; 

        _DISPIMP operator size_t () const;

        _DISPIMP size_t operator % (size_t dividend) const;

        _DISPIMP std::string to_string() const;

        _DISPIMP static const msg_guid null;

        GUID guid;
    private:
        msg_guid(int) { memset(&guid, 0, sizeof(guid)); }
    };

    /// <summary>
    /// Message envelope for individual blocks of a large message body.
    /// </summary>
    /// <remarks>
    /// Not having constructors allows this type to be used in a union,
    /// which we take advantage of in some of the code.
    /// </remarks>
    struct block_header
    {
        /// <summary>
        /// The overall block size, including header fields.
        /// </summary>
        /// <remarks>
        /// *** It is very important that this field remain the first in the structure ***
        /// </remarks>
        __int64  msg_length;   
        
        union 
        { 
            /// <summary>
            /// The uncompressed length holds the size of the message block before it was
            /// subjected to compression. A recipient performing a decompress should not
            /// see it expand beyond this size. In fact, expanding the data to any other
            /// size than this is an indication of message corruption.
            /// </summary>
            __int64 uncompressed_length;

            /// <summary>
            /// After a block is received, this pointer field may be used to link segments
            /// together.
            /// </summary>
            block_header *m_next; 
        };

        /// <summary>
        /// The kind of block this represents.
        /// </summary>
        msg_op  cmd;

        /// <summary>
        /// Flags applied to this block. See 'msg_flags' for more details.
        /// </summary>
        msg_flags flags;
    };

    /// <summary>
    /// Message envelope base class for most low-level messages passed between and within actors.
    /// </summary>
    struct dispatch_msg : block_header
    { 
        dispatch_msg() { this->cmd = CMD_NONE; this->uncompressed_length = 0; this->msg_length = sizeof(dispatch_msg); this->flags = MSGFLG_NONE; }
        dispatch_msg(msg_op cmd) { this->cmd = cmd; this->uncompressed_length = 0; this->msg_length = sizeof(dispatch_msg); this->flags = MSGFLG_NONE; }
        _DISPIMP ~dispatch_msg();
    };

    /// <summary>
    /// Envelope for all messages containing a GUID
    /// </summary>
    struct guid_msg : dispatch_msg
    {
        msg_guid    guid;
    };

    /// <summary>
    /// Envelope for all messages containing a GUID and a resource name, such as a type name or
    /// an address.
    /// </summary>
    struct general_msg : guid_msg
    {
        std::string name;

        bool Matches(const char *expected)
        {
            return strcmp(name.c_str(), expected) == 0;
        }
    };

    /// <summary>
    /// Envelope for all resource messages that carry data.
    /// </summary>
    struct remote_msg : general_msg
    {
        std::vector<byte> data;
    };

    /// <summary>
    /// Envelope for all resource messages that carry data within an address space.
    /// </summary>
    template<typename Payload>
    struct local_msg : public general_msg
    {
        Payload data;
    };


    /// <summary>
    /// Abstract base class for all implementations of compression algorithms.
    /// </summary>
    class compression
    {
    public:
        /// <summary>
        /// In-place compression of a message block.
        /// </summary>
        /// <param name="header">The message block header of the message that is to be compressed.</param>
        /// <param name="block">The address of the first byte to be compressed.</param>
        /// <param name="max">The maximum allowed size of the compressed data.</param>
        /// <returns>True if the block was compresses, false otherwise.</returns>
        /// <remarks>If the compression succeeds, the block's contents are overwritten, otherwise it is left untouched</remarks>
        virtual bool Compress(block_header &header, byte *block, size_t max) = 0; 

        /// <summary>
        /// In-place decompression of a message block.
        /// </summary>
        /// <param name="header">The message block header of the message that is to be compressed.</param>
        /// <remarks>The maximum decompressed size is held in block.uncompressed_size</remarks>
        virtual block_header *Decompress(const block_header &header) = 0;
    };

} // namespace actors

